package com.imon.web.codesphere;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
import org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion;
import org.springframework.stereotype.Component;

import com.imon.web.codesphere.data.RevisionChangeSet;
import com.imon.web.codesphere.data.RevisionInfo;

@Component
public class CodeJsonParser {

	private static final Log LOG = LogFactory.getLog(CodeJsonParser.class);

	private static final String COLON = ": ";
	private final JsonFactory jsonFactory;
	private ObjectMapper mapper;

	public CodeJsonParser() {
		jsonFactory = new JsonFactory();
		mapper = new ObjectMapper();

		mapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
		mapper.getSerializationConfig().setSerializationInclusion(Inclusion.NON_NULL);
		mapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
	}

	protected void overrideDefaultObjectMapper(final ObjectMapper objectMapper) {
		mapper = objectMapper;
	}

	public String getValueOfScalar(final String json, final String fieldName) {

		JsonParser jsonParser = null;

		try {
			jsonParser = jsonFactory.createJsonParser(json);
			if (jsonParser.nextToken() != JsonToken.START_OBJECT)
				throw new RuntimeException("Json string did not start with object");

			while (jsonParser.nextToken() != JsonToken.END_OBJECT) {
				final String name = jsonParser.getCurrentName();
				final JsonToken nextValue = jsonParser.nextToken();
				final String result = jsonParser.getText();
				if (!nextValue.isScalarValue())
					jsonParser.skipChildren();
				else if (name.equals(fieldName))
					return result;
			}
		} catch (final JsonParseException e) {
			LOG.error(e.getMessage() + COLON + e);
			throw new RuntimeException(e);
		} catch (final IOException e) {
			LOG.error(e.getMessage() + COLON + e);
			throw new RuntimeException(e);
		} finally {
			if (jsonParser != null)
				try {
					jsonParser.close();
				} catch (final IOException e) {
					LOG.error(e.getMessage() + COLON + e);
					throw new RuntimeException(e);
				}
		}
		return null;
	}

	@SuppressWarnings("unchecked")
	public Object getObject(final String json, final Class clazz) {
		try {
			return mapper.readValue(json, clazz);
		} catch (final IOException e) {
			LOG.error(e.getMessage() + COLON + e);
			throw new RuntimeException(e);
		}
	}

	public String getJson(final Object o) {
		LOG.debug(String.format("getJson for %s", o == null ? "null" : o.getClass()));
		final ByteArrayOutputStream out = new ByteArrayOutputStream();
		try {
			mapper.writeValue(out, o);
		} catch (final JsonGenerationException e) {
			LOG.error(e.getMessage() + COLON + e);
			throw new RuntimeException(e);
		} catch (final JsonMappingException e) {
			LOG.error(e.getMessage() + COLON + e);
			throw new RuntimeException(e);
		} catch (final IOException e) {
			LOG.error(e.getMessage() + COLON + e);
			throw new RuntimeException(e);
		}

		final String json = new String(out.toByteArray());
		return json;
	}

	public static void main(final String[] args) {
		final CodeJsonParser parser = new CodeJsonParser();
		final RevisionInfo revisionInfo = new RevisionInfo();
		revisionInfo.setRepositoryUrl("http://localhost");

		final RevisionChangeSet revisionChangeSet = new RevisionChangeSet();
		revisionChangeSet.setChangeType('A');
		revisionChangeSet.setPath("/svn/trunk");

		revisionInfo.getRevisionChanges().add(revisionChangeSet);

		final String json = parser.getJson(revisionInfo);
		System.out.println(json);
	}
}
